package org.huatek.anders.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.jni.User;
import org.huatek.anders.constants.Constants;
import org.huatek.anders.constants.Oauth2Constans;
import org.huatek.anders.constants.RedisKeys;
import org.huatek.anders.entity.OpenUserEntity;
import org.huatek.anders.entity.QqUserEntity;
import org.huatek.anders.entity.UserEntity;
import org.huatek.anders.entity.WechatUserEntity;
import org.huatek.anders.exception.ApiException;
import org.huatek.anders.mapper.Oauth2AccountMapper;
import org.huatek.anders.mapper.Oauth2Mapper;
import org.huatek.anders.mapper.Oauth2QqMapper;
import org.huatek.anders.mapper.Oauth2WechatMapper;
import org.huatek.anders.utils.BaseQueryWrapper;
import org.huatek.anders.utils.Md5;
import org.huatek.anders.utils.R;
import org.huatek.anders.utils.RedisUtils;
import org.huatek.anders.utils.jwt.JwtSubject;
import org.huatek.anders.utils.jwt.JwtToken;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.Map;

import static com.baomidou.mybatisplus.core.toolkit.Constants.MD5;

/**
 * @author anders
 * @date 2018-12-03 09:18
 **/
@Service
@Component
@Slf4j
public class Oauth2ServiceImpl extends ServiceImpl<Oauth2Mapper, UserEntity> implements Oauth2Service {

    private final RestTemplate restTemplate;
    private final RedisUtils redisUtils;
    private final Oauth2QqMapper qqMapper;
    private final Oauth2WechatMapper wechatMapper;
    private final Oauth2AccountMapper accountMapper;

    @Autowired
    public Oauth2ServiceImpl(RestTemplate restTemplate, RedisUtils redisUtils,Oauth2QqMapper qqMapper,Oauth2WechatMapper wechatMapper,Oauth2AccountMapper accountMapper){
        this.restTemplate = restTemplate;
        this.redisUtils = redisUtils;
        this.qqMapper = qqMapper;
        this.wechatMapper = wechatMapper;
        this.accountMapper = accountMapper;
    }
    @Value("${common.token.time}")
    private long tokenTime;

    private String getTokenValue(String tokenKey){
        return redisUtils.get(tokenKey);
    }


    private String createJwtToken(OpenUserEntity openUserEntity){
        if (openUserEntity.getUserId() == null || openUserEntity.getOpenid() == null){
            throw new ApiException("构造token主体时出错，userId或openId不能为null");
        }
        //重复登陆，直接返回原token
///        String oldToken = redisUtils.get(RedisKeys.OAUTH2_USER_TOKEN + openUserEntity.getUserId());
//        if (oldToken!=null){
//            return oldToken;
//        }
        //生成新token并放入redis
        JwtSubject sub = new JwtSubject(openUserEntity.getUserId().toString(), openUserEntity.getOpenid(), openUserEntity.getType(),tokenTime);
        String token = JwtToken.buildJWT(JwtToken.generalSubject(sub), tokenTime);
        redisUtils.set(RedisKeys.OAUTH2_USER + openUserEntity.getUserId(), openUserEntity);
        /*redisUtils.set(RedisKeys.OAUTH2_USER_TOKEN + openUserEntity.getUserId(), token, tokenTime);*/
        return token;
    }
    private Long bindAccount(OpenUserEntity openUserEntity){
        UserEntity userEntity = new UserEntity();
        userEntity.setLoginTime(new Date());
        if (StringUtils.isEmpty(openUserEntity)) {
            accountMapper.insert(userEntity);
        }else{
            userEntity.setId(openUserEntity.getUserId());
            accountMapper.updateById(userEntity);
        }
        return userEntity.getId();
    }


    @Override
    public R loginByAccount(OpenUserEntity openUserEntity) {
        BaseQueryWrapper<UserEntity> queryWrapper = new BaseQueryWrapper<>();
        if (!StringUtils.isEmpty(openUserEntity.getUserName())){
            queryWrapper.or().eq("user_name", openUserEntity.getUserName());
        }
        if (!StringUtils.isEmpty(openUserEntity.getMobile())){
            queryWrapper.or().eq("mobile", openUserEntity.getMobile());
        }
        UserEntity userEntity = getOne(queryWrapper);
        if (StringUtils.isEmpty(userEntity)){
            return R.error("登陆失败，帐号不存在");
        }else{
            String password = openUserEntity.getPassword();
            if (userEntity.getState()) {
                // 帐号锁定异常
                return R.error("登陆失败，帐号已禁用");
            }
            //密码规则 Md5.getMD5Code(Md5.getMD5Code("密码明文")+Md5.getMD5Code("用户名+slat"))
            if (!userEntity.getPassword().equals(Md5.getMD5Code(Md5.getMD5Code(password)+Md5.getMD5Code(userEntity.getCredentialsSalt())))){
                return R.error("登陆失败，密码不正确");
            }
            openUserEntity.setType("account");
            openUserEntity.setUserId(userEntity.getId());
            openUserEntity.setOpenid(userEntity.getUserName());
            //权限保存时候存储到redis 这里可以redis获取用户权限
            return R.ok().put("token", createJwtToken(openUserEntity));
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public R loginByQq(OpenUserEntity openUserEntity) {
        Oauth2Constans.Qq qqEntity = new Oauth2Constans.Qq();
        String openId = openUserEntity.getOpenid()==null?"":openUserEntity.getOpenid();
        String tokenKey = RedisKeys.OAUTH2_QQ_OPENID+openId;
        String tokenValue = getTokenValue(tokenKey);
        if (StringUtils.isEmpty(tokenValue)) {
            //step1:获取应用的授权码
            String tokenUrl = qqEntity.tokenFormat + openUserEntity.getCode();
            tokenValue = restTemplate.getForEntity(tokenUrl, String.class).getBody();
            //step2:获取用户的openId
            String openidUrl = qqEntity.openidUrl + tokenValue;
            openId = restTemplate.getForEntity(openidUrl, String.class).getBody();
            if(StringUtils.isEmpty(openId)){
                throw new ApiException("获取qq openId失败");
            }
            openId = openId.split("\"openid\":\"")[1].substring(0, 32);
            log.debug("qq:newOpenId = "+openId);
            tokenKey = RedisKeys.OAUTH2_QQ_OPENID+openId;
            //30天有效期
            redisUtils.set(tokenKey, tokenValue, Constants.QQ_TOKEN_TIME);
        }
        //首次登陆获取qq信息
        QqUserEntity qqUserEntity = new QqUserEntity();
        if (StringUtils.isEmpty(openUserEntity.getId())){
            //step3:使用用户的openid获取用户的信息
            String userInfoUrl = String.format(qqEntity.userInfoUrl, openId) + tokenValue;
            //获取用户信息
            qqUserEntity = restTemplate.getForEntity(userInfoUrl, QqUserEntity.class).getBody();
            if (qqUserEntity!=null) {
                qqUserEntity.setOpenid(openId);
                qqUserEntity.setUserId(bindAccount(openUserEntity));
                qqMapper.insert(qqUserEntity);
                BeanUtils.copyProperties(qqUserEntity, openUserEntity);
            }else{
                throw new ApiException("获取QQ用户信息错误");
            }
        }else{
            qqUserEntity.setOpenid(openId);
            qqUserEntity.setUserId(openUserEntity.getUserId());
            qqUserEntity.setUpdateTime(new Date());
            qqUserEntity.setUpdateUserId(openUserEntity.getId());
            qqMapper.updateById(qqUserEntity);
        }
        openUserEntity.setType("qq");
        return R.ok().put("token", createJwtToken(openUserEntity));
    }



    @Transactional(rollbackFor = Exception.class)
    @Override
    public R loginByWechat(OpenUserEntity openUserEntity) {
        Oauth2Constans.Wechat wechatEntity = new Oauth2Constans.Wechat();
        String openId = openUserEntity.getOpenid()==null?"":openUserEntity.getOpenid();
        String tokenKey = RedisKeys.OAUTH2_WECHAT_OPENID+openId;
        String tokenValue = getTokenValue(tokenKey);
        if (StringUtils.isEmpty(tokenValue)) {
            //step1:获取应用的授权码
            String tokenUrl = wechatEntity.tokenFormat + openUserEntity.getCode();
            Map tokenResp = restTemplate.getForEntity(tokenUrl, Map.class).getBody();
            if (tokenResp==null){
                throw new ApiException("获取wechat openId失败");
            }
            tokenValue = tokenResp.get("access_token").toString();
            openId = tokenResp.get("openid").toString();
            tokenKey = "wechat:"+openId;
            log.debug("wechat:newOpenId = "+openId);
            //7天有效期
            redisUtils.set(tokenKey, tokenValue, Constants.WECHAT_TOKEN_TIME);
        }
        WechatUserEntity wechatUserEntity = new WechatUserEntity();
        //首次登陆获取qq信息
        if (StringUtils.isEmpty(openUserEntity.getId())){
            //step2:使用用户的openid获取用户的信息
            String userInfoUrl = String.format(wechatEntity.userInfoUrl, openId, tokenValue);
            wechatUserEntity = restTemplate.getForEntity(userInfoUrl, WechatUserEntity.class).getBody();
            if (wechatUserEntity!=null){
                wechatUserEntity.setOpenid(openId);
                wechatMapper.insert(wechatUserEntity);
                BeanUtils.copyProperties(wechatUserEntity, openUserEntity);
            }else{
                throw new ApiException("获取微信用户信息错误");
            }
        }else{
            wechatUserEntity.setUserId(openUserEntity.getUserId());
            wechatUserEntity.setOpenid(openId);
            wechatUserEntity.setUpdateTime(new Date());
            wechatUserEntity.setUpdateUserId(openUserEntity.getId());
            wechatMapper.updateById(wechatUserEntity);
        }
        openUserEntity.setType("wechat");
        return R.ok().put("token", createJwtToken(openUserEntity));
    }

    @Override
    public R logoutUser(OpenUserEntity openUserEntity) {
        redisUtils.delete(RedisKeys.OAUTH2_USER + openUserEntity.getUserId());
        redisUtils.delete(RedisKeys.OAUTH2_USER_TOKEN + openUserEntity.getUserId());
        return R.ok("成功退出登陆");
    }

}
